package pm2

import (
	"chrent.com/ServiceManage/boot/ds"
	"chrent.com/ServiceManage/utils"
	"encoding/json"
	"fmt"
	"github.com/ntt360/pmon2/app/model"
	process2 "github.com/ntt360/pmon2/app/svc/process"
	"github.com/ntt360/pmon2/client/proxy"
	"github.com/pkg/errors"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"time"
)

func GetExecFile(execFile string) (string, error) {
	_, err := os.Stat(execFile)
	if os.IsNotExist(err) {
		return "", fmt.Errorf("%s not exist", execFile)
	}

	if path.IsAbs(execFile) {
		return execFile, nil
	}

	absPath, err := filepath.Abs(execFile)
	if err != nil {
		return "", fmt.Errorf("get file path error: %v", err.Error())
	}

	return absPath, nil
}

// check the process already have
func ProcessExist(execPath string) (*model.Process, bool) {
	var process model.Process
	err := ds.Db().First(&process, "process_file = ?", execPath).Error
	if err != nil {
		return nil, false
	}

	return &process, true
}
func LoadFirst(execPath string, flags string) ([]string, error) {
	data, err := proxy.RunProcess([]string{"start", execPath, flags})
	if err != nil {
		return nil, err
	}

	var tbData []string
	_ = json.Unmarshal(data, &tbData)

	return tbData, nil
}
func Restart(m *model.Process, flags string) ([]string, error) {
	// only stopped process or failed process allow run start
	if m.Status == model.StatusStopped || m.Status == model.StatusFailed {
		newData, err := ReloadProcess(m, flags)
		if err != nil {
			return nil, err
		}

		return newData, nil
	}

	return nil, fmt.Errorf("the process already start")
}
func ReloadProcess(m *model.Process, flags string) ([]string, error) {
	data, err := proxy.RunProcess([]string{"restart", m.ProcessFile, flags})

	if err != nil {
		return nil, err
	}

	var rel []string
	err = json.Unmarshal(data, &rel)
	if err != nil {
		return nil, err
	}

	return rel, nil
}

// 检查进程是否在运行中,进程的PID与系统真实进程是否匹配.
func Stop(id string, force bool) (*model.Process, error) {
	var process model.Process
	err := ds.Db().Where("id = ?", id).First(&process).Error
	if err != nil {
		return nil, err
	}
	if !utils.IsRunning(process.Pid, process.Name) {
		ds.Db().Save(&process)
		return &process, nil
	}
	// try to kill the process
	err = process2.TryStop(force, &process)
	if err != nil {
		return nil, err
	}
	return &process, nil
}
func ForceRestart(id string) (*model.Process, error) {
	var m *model.Process
	m, err := Stop(id, false)
	if err != nil { //正常退出失败后,强制退出
		if err = ds.Db().First(m, "id = ?", id).Error; err != nil {
			return nil, err
		}
		// checkout process state
		if process2.IsRunning(m.Pid) {
			if err = process2.TryStop(true, m); err != nil {
				return nil, err
			}
		}
	}
	rel, err := process2.TryStart(*m, "")
	if len(rel) < 4 {
		return nil, errors.Errorf("启动异常,TryStart返还异常!,rel:%v", rel)
	}
	m.Status = rel[3]
	if err != nil {
		return nil, err
	}
	time.Sleep(1 * time.Second)
	m.Pid, _ = strconv.Atoi(rel[2])
	if !process2.IsRunning(m.Pid) {
		return nil, errors.New("启动异常,请检查日志")
	}
	return m, nil
}
func FindProcessesByExe(target string) []int {
	var pids []int
	procDir := "/proc"
	entries, err := os.ReadDir(procDir)
	if err != nil {
		fmt.Printf("读取/proc失败: %v\n", err)
		return pids
	}
	for _, entry := range entries {
		if !entry.IsDir() {
			continue
		}
		pidStr := entry.Name()
		var pid int
		if pid, err = strconv.Atoi(pidStr); err != nil {
			continue // 跳过非数字目录
		}
		var exePath string
		if exePath, err = os.Readlink(filepath.Join(procDir, pidStr, "exe")); err != nil {
			continue // 忽略无法访问的进程
		}
		if exePath == target {
			pids = append(pids, pid)
		}
	}
	return pids
}
func ReloadStatus() {
	defer func() {
		recover()
	}()
	all := []model.Process{}
	ds.Db().Find(&all)
	for _, process := range all {
		pids := FindProcessesByExe(process.ProcessFile)
		if len(pids) == 0 {
			process.Status = model.StatusStopped
			ds.Db().Save(&process)
			continue
		}
		process.Status = model.StatusRunning
		process.Pid = pids[0]
		ds.Db().Save(&process)
	}
}
